Subtrees for dynamic objects
Since ChromeEngine uses BSP for it's sorting traditionally the limitation is you can only have static scenes supported, however in ChromeEngine we've gone to great lengths to provide a workaround for dynamic objects. Our solution is called "BSP subtrees". And we'll walk you through how they work.
#
What are subtrees?#
Reinserting point primitivesRe-inserting a point into a BSP tree is always O(log n) time complexity and easy to do. Here's how we do it:
- Point primitives are always leaf nodes of the BSP tree
- So removing a point primitive node from the tree is as simple as snipping off the leaf
- Finally to complete the reinsertion we just insert the point into the tree by performing a fast traversal specific to points, without any split checks.
#
Subtrees for dynamic objects contained within point primitivesif we can sort point primitives into a bsp tree could we simply tree a models tree as point and then sort an entire object into the scene? Yes! To do this we introduce a new primitive called a container
and we introduce the idea of dynamic objects
. Here's what we do:
- Mark objects as being
dynamic
- Generate each
dynamic object
their own subtree - Create a
container primitive
for the dynamic object, which is treated as point primitive except also points to a subtree. - We can now insert this contain primitive into the main tree, and when it is reached during traversal we recursively traverse it's subtree.
Dynamic objects
#
The big downside with Dynamic objects work great in alot of cases, but the problem is since we tree the object as a point and don't split any primitives, there's a possibility that the objects primitives will be sorted wrong and this can cause visual bugs. We are trying to solve this but it's not easy.
#
Custom Block DescriptionsThese are the custom blocks in ChromeEngine which deal with dynamic objects:
handle_dynamic_objects
bsp.tree.insert_prim
bsp.tree.try_to_insert_in_container
bsp.tree.reinsert_updated_objects
bsp.tree.remove_leaf_node
bsp.tree.remove_prim_from_node
handle_dynamic_objects
#
define bsp.handle_dynamic_objects \( \)
- Creates a root node if the tree is empty
- this only occurs if all objects are marked as dynamic
- Iterate over all the gameobjects and for each object marked as dynamic add it to the
@dynamic_objects
list and to the_dynamic_obj_queue
- Whilst
_dynamic_obj_queue
still has items:- Pop off the first object
_*obj
off the queue - If
_*obj
is not contained within the main tree (aka is a child of a dynamic object) and it's parent dynamic object has not been generated yet, add it back to the queue and go back to step 1. - Create a new vertex to denote the centre of the
container
primitive. The position is equal to the bounding sphere of_*obj
's centre coordinate plus the dynamic offset vector. - For each primitive assigned to
_*obj
insert pointers to them at the end of the_*prims
list._lower
and_**prim
are pointers to the start and end of the newly added sublist of_*prims
. - Generate a BSP subtree using this sublist.
- Create a
container
primitive using the previously created vertex, the radius of the dynamic objects bounding sphere and a pointer to the newly created subtree. Insert thiscontainer
primitive into the main tree or the subtree assigned to the dynamic object usingbsp.tree.insert_prim
- Pop off the first object
bsp.tree.insert_prim
#
define bsp.tree.insert_prim \((_*prim)(old_**prim)(*root)(orientation) level (level) local_var_stack (stack:current)\)
Performs the insertion of a primitive into a tree. The steps are:
- Traverse the tree until we reach a point primitive node or a leaf node to find where the point primitive pointed to by
_*prim
should be inserted - If the current node is Empty, then we reached a leaf node which is assigned to a polygon:
- insert the primitive pointer into the
_*prims
list at the end - Create a new leaf node as a child of the leaf node to store our new primitive. Whether it's a left or right node depends on orientation.
- insert the primitive pointer into the
- Else:
- Try to insert the primitive into a
container
primitive in case there is one usingbsp.tree.try_to_insert_in_container
. - If our current node was previously node (which we can tell since it would've been marked as a TOMBSTONE) then insert our primitive pointer into the
_*prims
list at the end - Else insert our primitive pointer in the same location as the first point primitive stored by the current node.
- Try to insert the primitive into a
bsp.tree.try_to_insert_in_container
#
define _tmp = bsp.tree.try_to_insert_in_container \( *prim (*prim) old_**prim (old_**prim) orientation (orientation) level (level) \)
Iterates over the point primitives stored in a given BSP tree node (it assumes _current
is set to the current node), and checks if they are containers. If there's more than one container it finds the closest container (by bounding sphere distance) to the primitive being inserted. If any container was found it inserts the primitive into it. It sets _tmp
to 1 if it was successful else 0.
bsp.tree.reinsert_updated_objects
#
define bsp.tree.reinsert_updated_objects
- For each updated object which is dynamic:
- remove the primitive from it's BSP tree node using
bsp.tree.remove_prim_from_node
- pop it's prim pointer from
prims
- insert the primitive back into the tree using
bsp.tree.insert_prim
- remove the primitive from it's BSP tree node using
bsp.tree.remove_prim_from_node
#
define _**prim = bsp.remove_prim_from_node \((*node) (*prim)\)
- If
_*prim
is the only primitive in it's tree node then callbsp.tree.remove_leaf_node
to snip off the leaf node (we can assume it's a leaf node as we only reinsert point primitives which are always leaves) - Else:
- Find the index of
_*prim
in the sublist of_*prims
assigned to the tree node and then adjust the BSP tree to not include it. Note: we don't remove the primitive, from the list as we're going to add it back anyway and we don't want to waste computation.
- Find the index of
bsp.tree.remove_leaf_node
#
define bsp.remove_leaf_node \((*node)\)
Removes a leaf node by removing all references to it within the bsp.tree.
- Remove edges to the node
- If the node is at the end of the
bsp.tree
list simply delete the data, otherwise to preserve pointers just assign the first item of data to be TOMBSTONE to mark that it was deleted.